package jobs

import (
	"fmt"
	"os"
	"strconv"
	"youbei/dao"
	rs "youbei/utils/rs"

	"github.com/beego/beego/v2/adapter/httplib"
	"github.com/segmentio/ksuid"
)

// ExecuteRemoteFileTransfer 根据指定的远程存储配置，执行本地文件到远程存储的上传
// remoteConfig: 远程存储的配置
// localFilePath: 本地文件的路径
// 返回值: 错误信息
func ExecuteRemoteFileTransfer(remoteConfig dao.RemoteStorage, localFilePath string, lid string, wl dao.Whitelist) error {
	// 初始化一个远程存储对象
	remoteStorage := rs.NewRS(remoteConfig.Host, remoteConfig.Port, remoteConfig.Username, remoteConfig.Password, localFilePath, remoteConfig.Path)

	rlog := dao.Rlog{}
	rlog.ID = remoteConfig.Rlogid
	// 根据远程存储的类型，执行不同的上传操作
	switch remoteConfig.Types {
	case "ftp":
		// 执行FTP上传
		cols := []string{"status"}
		err := remoteStorage.FtpUpload()
		if err != nil {
			cols = append(cols, "msg")
			rlog.Status = 2
			rlog.Msg = err.Error()
		} else {
			rlog.Status = 0
		}
		postRLog(wl, &rlog, cols)
		return err

	case "sftp":
		// 初始化一个SFTP对象
		cols := []string{"status"}
		sftp, err := remoteStorage.NewSftp()
		if err != nil {
			cols = append(cols, "msg")
			rlog.Status = 2
			rlog.Msg = err.Error()

		} else {
			// 执行SFTP上传
			err = sftp.Upload()
			if err != nil {
				cols = append(cols, "msg")
				rlog.Status = 2
				rlog.Msg = err.Error()
			}
			rlog.Status = 0
		}
		postRLog(wl, &rlog, cols)
		return err

	case "Yserver":
		// 读取大文件，生成文件分片
		filePackets, err := remoteStorage.ReadBigFile()
		if err != nil {
			return err
		}
		// 创建一个文件上传日志对象
		uploadFileLog := &dao.YsUploadFile{
			ID:                 ksuid.New().String(),
			SrcFilePath:        filePackets.SrcFilePath,
			UploadFileServerID: filePackets.UploadFileServerID,
			Size:               filePackets.Size,
			PacketNum:          filePackets.PacketNum,
			Lid:                lid,
			RLid:               remoteConfig.Rlogid,
		}

		// 添加文件上传日志
		if err = AddYsFileLog(wl, uploadFileLog); err != nil {
			return err
		}

		// 读取并上传所有的文件分片
		allDoneErr := 0
		packetLogs := []dao.YsPacket{}
		for _, packet := range filePackets.Packets {
			// 创建一个分片上传日志对象
			packetLog := dao.YsPacket{
				ID:              ksuid.New().String(),
				Yid:             uploadFileLog.ID,
				Offset:          packet.Offset,
				Status:          1,
				SortID:          packet.SortID,
				SrcPacketPath:   packet.Packetpath,
				UploadPacketURL: filePackets.PacketUploadURL + strconv.Itoa(packet.SortID),
			}
			packetLogs = append(packetLogs, packetLog)
		}

		if len(packetLogs) == 0 {
			return nil
		}
		if err := AddYsPacketLog(wl, packetLogs); err != nil {
			return err
		}
		// 打开源文件
		srcFile, err := os.OpenFile(filePackets.SrcFilePath, os.O_RDONLY, os.ModePerm)
		if err != nil {
			cols := []string{`status`}
			cols = append(cols, "msg")
			rlog.Status = 2
			rlog.Msg = err.Error()
			postRLog(wl, &rlog, cols)
			return err
		}

		defer srcFile.Close()

		for _, packet := range packetLogs {
			// 创建并上传文件分片
			if err := filePackets.CreatePacket(srcFile, packet); err != nil {
				packet.Status = 2
				packet.Msg = err.Error()
				cols := []string{"status", "msg"}
				updateYsPacketLog(wl, packet, cols)
				allDoneErr++
			} else {
				cols := []string{"status"}
				packet.Status = 0
				updateYsPacketLog(wl, packet, cols)
			}
		}

		// 根据文件分片的上传结果，更新文件的上传状态
		cols := []string{`status`}
		if allDoneErr > 0 {
			cols = append(cols, "msg")
			rlog.Status = 2
			rlog.Msg = err.Error()
		} else {
			rlog.Status = 0
		}

		postRLog(wl, &rlog, cols)
		return err

	default:
		return fmt.Errorf("unsupported remote storage type: %s", remoteConfig.Types)
	}
}

type respRLog struct {
	Cols []string `json:"cols"`
	RLog dao.Rlog `json:"rlog"`
}

func postRLog(wl dao.Whitelist, rlog *dao.Rlog, cols []string) {
	reprlog := respRLog{}
	req := httplib.Post("http://" + wl.ServerIP + ":" + strconv.Itoa(wl.ServerPort) + "/client/updaterlog")
	req.Header("token", wl.Token)
	req.Header("Content-Type", "application/json")
	reprlog.Cols = cols
	reprlog.RLog = *rlog
	req.JSONBody(reprlog)
	req.String()
}

func AddYsFileLog(wl dao.Whitelist, ysfilelog *dao.YsUploadFile) error {
	req := httplib.Post("http://" + wl.ServerIP + ":" + strconv.Itoa(wl.ServerPort) + "/client/addyslog")
	req.Header("token", wl.Token)
	req.Header("Content-Type", "application/json")
	req.JSONBody(ysfilelog)
	_, err := req.String()
	return err
}

func AddYsPacketLog(wl dao.Whitelist, yspacket []dao.YsPacket) error {
	req := httplib.Post("http://" + wl.ServerIP + ":" + strconv.Itoa(wl.ServerPort) + "/client/addyspacketlog")
	req.Header("token", wl.Token)
	req.Header("Content-Type", "application/json")
	req.JSONBody(yspacket)
	_, err := req.String()
	return err
}

type respPacketLog struct {
	Cols      []string     `json:"cols"`
	PacketLog dao.YsPacket `json:"packetlog"`
}

func updateYsPacketLog(wl dao.Whitelist, yspacket dao.YsPacket, cols []string) error {
	resppacketlog := respPacketLog{}
	req := httplib.Post("http://" + wl.ServerIP + ":" + strconv.Itoa(wl.ServerPort) + "/client/updateyspacketlog")
	req.Header("token", wl.Token)
	req.Header("Content-Type", "application/json")
	resppacketlog.Cols = cols
	resppacketlog.PacketLog = yspacket
	req.JSONBody(resppacketlog)
	_, err := req.String()
	return err
}
